home *** CD-ROM | disk | FTP | other *** search
/ CGI How-To / CGI HOW-TO.iso / chap5 / 5_3 / input_c / parsehtm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-15  |  10.4 KB  |  482 lines

  1.  
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include "parsehtm.h"
  5.  
  6. static FILE *htmlFile = 0;
  7.  
  8. /* Global Dictionaries */
  9. Dictionary endTags;
  10. Dictionary handlerDict;
  11.  
  12. /*
  13.  * Tag Handlers are functions that return void,
  14.  * and take four arguements,
  15.  * 1) the tag string, which they should alter to
  16.  * the parsed text.
  17.  * 2) an arg string, not containing any registered endTags
  18.  * 3) any registered endTag
  19.  * 4) a dictionary of data for the tag, from dictForTag()
  20.  * The keys for tagDict are const char *,and the values are String
  21.  *
  22.  * The prototype for a handler is:
  23.  * void handler(String ts,String as,String et,Dictionary td);
  24.  */
  25.  
  26. /* This function allocates space for the global dictionaries
  27.  * it must be called before any handlers or end tags are registered. */
  28. void initializeHtmlParsingLibrary()
  29. {
  30.     endTags = dict_alloc();
  31.     handlerDict = dict_alloc();
  32. }
  33.  
  34. /*
  35.  * parseHtml() provides the primary interface
  36.  * to this library. It takes the file name
  37.  * and returns a String containing the parsed html.
  38.  */
  39.  
  40. String parseHtml(const char *fileName)
  41. {
  42.     String retVal;
  43.     
  44.     if(fileName && *fileName)
  45.     {
  46.         htmlFile = fopen(fileName,"r");
  47.     }
  48.     
  49.     if(htmlFile)
  50.     {
  51.         retVal = mainHtmlParser((const char *) 0);
  52.         fclose(htmlFile);
  53.     }
  54.     
  55.     return retVal;
  56. }
  57.  
  58. /*
  59.  * mainHtmlParser is at the heart of the parsing code
  60.  * This function loops over the file, stopping at the end
  61.  * of the file, a stop character or a stop string.
  62.  * It seperates the file into tags and plain text,
  63.  * then calls the functions handleTag and handlePlainText
  64.  * to parse the data.
  65.  */
  66. String mainHtmlParser(const char *stopStr)
  67. {
  68.     char c,stopChar;
  69.     String tmpBuffer = 0;
  70.     String mainBuffer = 0;
  71.     int inTag = 0;
  72.     
  73.     /* Initialize the buffers. */
  74.     tmpBuffer = string_alloc(64);
  75.     mainBuffer = string_alloc(4096);
  76.     
  77.     /* If we got a stop string, see if
  78.      * it is really a character. This allows
  79.      * the function to have dual functionality off
  80.      * of only one argument.
  81.      */
  82.     if(stopStr)
  83.     {
  84.         if(*stopStr && (strlen(stopStr) == 1))
  85.         {
  86.             stopChar = stopStr[0];
  87.         }
  88.         else
  89.         {
  90.             stopChar = '\0';
  91.         }
  92.     }
  93.     
  94.     /* Loop to the end of the file. */
  95.     while(!feof(htmlFile))
  96.     {
  97.         /* Get a character at each step of the loop */
  98.         c = fgetc(htmlFile);
  99.         
  100.         /* See what character this is*/
  101.         
  102.         if(c == '>')/* end of a tag */
  103.         {
  104.             if(!inTag)/*we are not in a tag, this is an error*/
  105.             {
  106.                 exit(1);
  107.             }
  108.             
  109.             /* Close the tag */
  110.             inTag = 0;
  111.             
  112.             string_appendChar(tmpBuffer,c);
  113.             
  114.             /* See if this tag is the stop string */
  115.             
  116.             if(stopStr && !strcmp(tmpBuffer->string,stopStr))
  117.             {
  118.                 return mainBuffer;
  119.             }
  120.             else
  121.             {
  122.                 /* Handle the tag we just found */
  123.                 handleTag(tmpBuffer);
  124.                 
  125.                 /* Add the parsed text to the main buffer */
  126.                 string_appendString(mainBuffer,tmpBuffer->string);
  127.                 
  128.                 /* Reset the tmp buffer */
  129.                 string_empty(tmpBuffer);
  130.             }
  131.         }
  132.         else if(c == '<')/* Start of a tag */
  133.         {
  134.             if(inTag)/*we are already in a tag, this is an error*/
  135.             {
  136.                 exit(1);
  137.             }
  138.             
  139.             inTag = 1;
  140.             
  141.             /* If we have some data, it is plain text,
  142.                 * add it to the main buffer.
  143.                 */
  144.             if(tmpBuffer->string[0])
  145.             {
  146.                 handlePlainText(tmpBuffer);
  147.                 
  148.                 /* Add the parsed text to the main buffer */
  149.                 string_appendString(mainBuffer,tmpBuffer->string);
  150.                 
  151.                 /* Reset the tmp buffer */
  152.                 string_empty(tmpBuffer);
  153.             
  154.             }
  155.             
  156.             /* Reset the tmp buffer */
  157.             string_setStringValue(tmpBuffer,"<");
  158.         }
  159.         else if(c == stopChar)/* Found the stop char */
  160.         {
  161.             string_appendChar(tmpBuffer,c);
  162.             
  163.             /* Add the parsed text to the main buffer */
  164.             string_appendString(mainBuffer,tmpBuffer->string);
  165.             
  166.             break;
  167.         }    
  168.         else/* normal character */
  169.         {
  170.             string_appendChar(tmpBuffer,c);
  171.         }
  172.     }
  173.     
  174.     /* free the tmp buffer */
  175.     string_free(tmpBuffer);
  176.  
  177.     /* Return the parsed text */
  178.     return mainBuffer;
  179. }
  180.  
  181. /*
  182.  * Handle tag is called by mainHtmlParser.
  183.  * This function looks for a tag handler and calls
  184.  * it with the appropriate arguments.
  185.  */
  186. void handleTag(String tagString)
  187. {
  188.     Dictionary tagDict = 0;
  189.     String argString = 0;
  190.     String endTag = 0;
  191.     void (*handler)();
  192.     String tag;
  193.     
  194.     /* Create a dictionary for the tag 
  195.      * This will make it easier to access
  196.      * information about the tag
  197.      */
  198.     tagDict = dictForTag(tagString);
  199.     
  200.     /* The tag dictionary should have a TAG value */
  201.     if(tag = (String)dict_valueForKey(tagDict,"TAG"))
  202.     {
  203.         if(tag->string)
  204.         {
  205.             /* See if there is an endtag or a handler registered*/
  206.             endTag = dict_valueForKey(endTags, tag->string);
  207.             
  208.             handler = dict_valueForKey(handlerDict, tag->string);
  209.         }
  210.     }
  211.     
  212.     /* If there is a handler, use it */
  213.     if(handler)
  214.     {
  215.         /* If there is an end tag, parse up to it
  216.          * be sure to remove the end tag from the body text.
  217.          */
  218.         if(endTag && endTag->string)
  219.         {
  220.             char * finalPointy = 0;
  221.             
  222.             argString = mainHtmlParser(endTag->string);
  223.             
  224.             /* Get rid of the end tag */
  225.             
  226.             if(endTag->string[0] == '<')/* this is a real tag */
  227.             {
  228.                 finalPointy = strrchr(argString->string,'<');
  229.                 
  230.                 /* End the string at the final <, thus removing the end tag */
  231.                 if(finalPointy) *finalPointy = '\0';
  232.             }
  233.         }
  234.         
  235.         /* Call the handler */
  236.         handler(tagString,argString,endTag,tagDict);
  237.     }
  238.     else /* No handler, treat this as plain text */
  239.     {
  240.         handlePlainText(tagString);
  241.     }
  242.     
  243.     /* Clean up */
  244.     dict_freeWithData(tagDict,string_free);
  245.     string_free(argString);
  246. }
  247.  
  248. void handlePlainText(String plainText)
  249. {
  250.     void (*handler)();
  251.     
  252.     /* See if there is a default handler */
  253.     handler = dict_valueForKey(handlerDict,"DEFAULT");
  254.     
  255.     /* If there is a default handler, use it*/
  256.     if(handler)
  257.     {
  258.         handler(plainText,0,0,0);
  259.     }
  260. }
  261.  
  262. /*
  263.  * Convienence function that searches a c string for a tag
  264.  * it returns a pointer into the string, after the tag.
  265.  * This return value is used for followup calls.
  266.  */
  267.  
  268. char * nextTag(char *start, String tagString)
  269. {
  270.     int inQuote = 0;
  271.     
  272.     if(start && *start)
  273.     {
  274.         /* Move past any starting delimiters */
  275.         
  276.         while((*start == ' ')||(*start == '<'))
  277.         {
  278.             start++;
  279.         }
  280.         
  281.         string_empty(tagString);
  282.         
  283.         while(*start && (((*start != ' ')&&(*start != '>'))||inQuote))
  284.         {
  285.             if(*start == '"')/*Look for quotes, dont include them*/
  286.             {
  287.                 if(inQuote) inQuote = 0;
  288.                 else inQuote=1;
  289.             }
  290.             else
  291.             {
  292.                 string_appendChar(tagString,*start);
  293.             }
  294.             start++;
  295.         }
  296.         
  297.         start++;/*Move to next character for subsequent next search*/
  298.     }
  299.     
  300.     return start;
  301. }
  302.  
  303. /*
  304.  * dictForTag is a convience function
  305.  * it takes a tag string, and returns
  306.  * a dictionary of key value pairs.
  307.  * One key should be TAG. All of the keys
  308.  * are capitalized.
  309.  */
  310. Dictionary dictForTag(String tagString)
  311. {
  312.     Dictionary tagDict = 0;
  313.     String tag;
  314.     String tmpString;
  315.     char *slide = (char *)0;
  316.     char *key,*value;
  317.     String dataToAdd;
  318.  
  319.     /* Make the dictionary */
  320.     tagDict = dict_alloc();
  321.     
  322.     /* If there is no tag string, return the empty dict */
  323.     if(!tagString || !(tagString->string)) return tagDict;
  324.     
  325.     /* Copy the tag string, since strtok will alter data */
  326.     tag = string_alloc(strlen(tagString->string) + 1);
  327.     string_setStringValue(tag, tagString->string);
  328.     
  329.     /* Make a temporary string, to help with our work */
  330.     tmpString = string_alloc(strlen(tagString->string) + 1);
  331.     
  332.     /*Get the first token*/
  333.     slide = nextTag(tag->string,tmpString);
  334.     
  335.     /* If the current token starts with a <, get rid of the < */
  336.     if(tmpString->string && (tmpString->string[0] == '<'))
  337.     {
  338.         string_crop(tmpString,1);
  339.     }
  340.     
  341.     /* Capitalize the tag string, and add it to the dict */
  342.     if(tmpString->string)
  343.     {
  344.         string_toUpper(tmpString);
  345.         
  346.         dataToAdd = string_alloc(strlen(tmpString->string) +1);
  347.         string_setStringValue(dataToAdd, tmpString->string);
  348.         
  349.         dict_setValueForKey(tagDict,"TAG", dataToAdd);
  350.     }
  351.     
  352.     /* Loop over the tag string, breaking it into tokens */
  353.     do
  354.     {
  355.         
  356.         /*Get the next token*/
  357.         slide = nextTag(slide,tmpString);
  358.         
  359.         /* If we got a token */
  360.         if(tmpString->string)
  361.         {
  362.             /* See if it is binary or unary */
  363.             if(value = strchr(tmpString->string,'='))
  364.             {
  365.                 key = tmpString->string;
  366.                 
  367.                 *value = '\0'; /* set the = to a null */
  368.                 value++; /* Move value past the old = */
  369.                 
  370.                 string_toUpper(tmpString);/* Will only get the key */
  371.                 
  372.                 /* Clean up any trailing or leading quotes */
  373.                 if(value[strlen(value)-1] == '\"')
  374.                 {
  375.                     value[strlen(value)-1] = '\0';
  376.                 }
  377.                 
  378.                 if(value[0] == '\"')
  379.                 {
  380.                     value++;
  381.                 }
  382.                 
  383.                 /* Add the attribute to the tag dict */
  384.                 dataToAdd = string_alloc(strlen(value) +1);
  385.                 string_setStringValue(dataToAdd,value);
  386.                 
  387.                 dict_setValueForKey(tagDict, key, dataToAdd);
  388.             }
  389.             else
  390.             {
  391.                 if(tmpString->string[strlen(tmpString->string)-1] == '\"')
  392.                 {
  393.                     string_chop(tmpString,1);
  394.                 }
  395.                 
  396.                 if(tmpString->string[0] == '\"')
  397.                 {
  398.                     string_crop(tmpString,1);
  399.                 }
  400.                 
  401.                 string_toUpper(tmpString);
  402.                 
  403.                 /* Add the unary attribute to the tag dict */
  404.                 
  405.                 dataToAdd = string_alloc(strlen(tmpString->string) +1);
  406.                 string_setStringValue(dataToAdd, tmpString->string);
  407.                 
  408.                 dict_setValueForKey(tagDict, tmpString->string, dataToAdd);
  409.             }
  410.         }
  411.         
  412.     }while (slide && *slide);
  413.     
  414.     string_free(tag);
  415.     string_free(tmpString);
  416.     
  417.     return tagDict;
  418. }
  419.  
  420. /*
  421.  * stringForTagDict is provided for programmers
  422.  * writing handler functions.
  423.  * This function takes a tag dictionary
  424.  * and returns the cooresponding tag string.
  425.  * the string is allocated, and is the callers
  426.  * responsibility.
  427.  */
  428. String stringForTagDict(Dictionary tagDict)
  429. {
  430.     String tagString;
  431.     DictState iterator;
  432.     const char *key = 0;
  433.     String value = 0;
  434.  
  435.     /* Allocate the string */
  436.     tagString = string_alloc(64);
  437.     
  438.     /* Make sure we have tag data */
  439.     if(tagDict)
  440.     {
  441.         /* Add the tag to the string */
  442.         value = dict_valueForKey(tagDict,"TAG");
  443.         
  444.         if(value && value->string)
  445.         {
  446.             string_appendString(tagString,"<");
  447.             string_appendString(tagString,value->string);
  448.         
  449.             /* Loop over the dictionary, adding tag attributes */
  450.             iterator = dict_initState(tagDict);
  451.                         
  452.             while(dict_nextState(&iterator))
  453.             {
  454.                 key = (const char *) iterator.curNode->key;
  455.                 value = (String) iterator.curNode->value;
  456.                 
  457.                 if(strcmp(key,"TAG") != 0)/* Ignore the tag */
  458.                 {
  459.                     if(!strcmp(key,value->string))/* This is a unary key */
  460.                     {
  461.                         string_appendString(tagString," ");
  462.                         string_appendString(tagString,key);
  463.                     }
  464.                     else /* Binary attribute */
  465.                     {
  466.                         string_appendString(tagString," ");
  467.                         string_appendString(tagString,key);
  468.                         string_appendString(tagString,"=\"");
  469.                         string_appendString(tagString,value->string);
  470.                         string_appendString(tagString,"\"");
  471.                     }
  472.                 }
  473.             }
  474.  
  475.             /* Close the tag string */
  476.             string_appendString(tagString,">");
  477.         }
  478.     }
  479.     /* Return the string */
  480.     return tagString;
  481. }
  482.